package cc.yooho.framework.lock.redislock;


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * @author shitiecheng
 * @version v1
 * @summary 分布式锁管理
 * @Copyright (c) 2017, Lianjia Group All Rights Reserved.
 * @since 2018/6/10
 */
@Aspect
@Component
public class LockManager {
    private static Logger logger = LoggerFactory.getLogger(LockManager.class);

    @Autowired
    private RedisTemplate redisTemplate;

    @Around(value = "@annotation(lock)", argNames = "pjp, lock")
    public Object around(ProceedingJoinPoint pjp, Lock lock) throws Throwable {
        Class clazz = pjp.getTarget().getClass();
        String methodName = pjp.getSignature().getName();
        //获取参数
        Object[] args = pjp.getArgs();
        MethodSignature ms = (MethodSignature) pjp.getSignature();
        Method m = ms.getMethod();

        String keyPrix = lock.keyPrex();
        if (keyPrix.equals("")) {
            keyPrix = clazz.getName()+ ":" + methodName + ":";
        }

        //分布式锁的key
        StringBuilder lockKey = new StringBuilder("lock:" + keyPrix);
        //获取加注解的方法参数的值
        Annotation[][] parameterAnnotations = m.getParameterAnnotations();
        for (int i = 0; i < parameterAnnotations.length; i++) {
            Annotation[] annotations = parameterAnnotations[i];
            for (Annotation annotation : annotations) {
                if (annotation.annotationType() == LockKey.class) {
                    lockKey.append("_");
                    lockKey.append(args[i]);
                }
            }
        }

        RedisLock redisLock = new RedisLock(redisTemplate, lockKey.toString(), lock.lockTime());
        Object result;
        // 获得锁
        if (redisLock.lock()) {
            try {
                result = pjp.proceed();
            } catch (Exception e) {
                throw e;
            } finally {
                if (!redisLock.unlock()) {
                    logger.warn("释放分布式锁失败, key=" + lockKey.toString());
                }
            }
        } else {
            throw new RuntimeException("获取分布式锁失败, key=" + lockKey.toString());
        }

        return result;
    }
}
